#ifndef __LIB_MGR__
#define __LIB_MGR__

#include "../file/osfop.h"
#include "mfb.h"

namespace xm
{
	struct FileMapping : refable_impl_t
	{
		iptr<IKNativeFile>	_NativFile;
		HANDLE				_MapHandle;

		FileMapping() : _MapHandle(0)
		{
		}
		virtual ~FileMapping()
		{
			if( _MapHandle ) CloseHandle(_MapHandle);
		}
	};

	template < class T, class K >
	static inline T __align_big( T v, K a )
	{
		bool pad = ( v % a ) != 0;
		return (T)(v/a*a + (pad?a:0));
	}


	class MdTable : public IKMDRTable, public IKBStream, UnkBase
	{
	protected:

		refp<FileMapping>	_mapping;
		LPCVOID				_data;
		SIZE_T				_bytes;
		UINT64				_pointer;
	public:

		MdTable() : _data(0), _bytes(0),_pointer(0)
		{}

		~MdTable()
		{
			if( _mapping && _data && _bytes )
			{
				::UnmapViewOfFile( _data );
			}
		}

		UNKNOWN_IMP2_(IKMDRTable,IKBStream);

		HRESULT init( FileMapping * mapping, mfb::ifsect_t * sect  )
		{
			uint64_t mapb = sect->offset & (~0xFFFF);
			uint64_t mape = sect->offset + sect->bytes;

			LPCVOID vp = ::MapViewOfFileEx( mapping->_MapHandle, FILE_MAP_READ, 
				(DWORD)(mapb>>32), (DWORD)mapb, (SIZE_T)(mape-mapb), 0 );
			if( !vp ) return E_FAIL;

			_data = (char*)vp + (sect->offset - mapb);
			_bytes = sect->bytes;
			_mapping = mapping;

			return S_OK;
		}

		STDMETHOD(GetTableData)( cblob_t & data )
		{
			data.Data = _data;
			data.Length = _bytes;

			return S_OK;
		}
		STDMETHOD_(BOOL,IsSameTable)( IKMDRTable * op )
		{
			return (IKMDRTable*)this == op;
		}

		//////////////////////////////////////////////////////////////////////////

		STDMETHOD_(BOOL,IsVirtual)() { return TRUE; };
		STDMETHOD(Write)( LPCVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff ) { return E_ACCESSDENIED; };
		STDMETHOD(Writep)( UINT64 uliPos, LPCVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff ) { return E_ACCESSDENIED; };
		STDMETHOD(Truncate)() { return E_ACCESSDENIED; };

		STDMETHOD(Read)( LPVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff )
		{
			return Readp( _pointer, lpBuffer, cbData, lpEff );
		}
		STDMETHOD(Seek)( INT64 liDistance, SEEK_FORM emFrom )
		{
			if( emFrom == FROM_BEGIN )
				_pointer = liDistance;
			else if( emFrom == FROM_CUR )
				_pointer += liDistance;
			else if( emFrom == FROM_END )
				_pointer = _bytes + liDistance;
			else
				return E_INVALIDARG;
			return S_OK;
		}
		STDMETHOD(Tell)( UINT64 * puliPointer )
		{
			if(puliPointer) *puliPointer = _pointer;
			return S_OK;
		}
		STDMETHOD(Readp)( UINT64 uliPos, LPVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff )
		{
			if( lpEff ) *lpEff = 0;
			if( uliPos >= _bytes ) 
				return S_OK;

			if( !cbData )
				return S_OK;

			size_t want = MIN( (_bytes - uliPos), cbData );

			size_t off = (size_t)uliPos;
			memcpy( lpBuffer, (LPCSTR)_data + off, want );

			_pointer = uliPos + want;

			return S_OK;
		}
		STDMETHOD(SetPos)( UINT64 uliPos )
		{
			_pointer = uliPos;
			return S_OK;
		}
		STDMETHOD_(UINT64,Length)()
		{
			return _bytes;
		}
	};


	class MDTManager : public IKMDTManager, UnkBase
	{
	public:
		
		typedef mfb::reader_t< optr<MdTable> >	TabReader;

	protected:

		refp<FileMapping>	_mapping;

		TabReader			_reader;

	public:
		
		UNKNOWN_IMP1(IKMDTManager);

		DEFAULT_INIT_CLASS();

		MDTManager()
		{
			
		}

		STDMETHOD(LoadFromFile)( LPCSTR aFileName, UINT32 opt )
		{
			iptr<IKNativeFile> nativ( PROGID_WinNativeFile );
			if( !nativ ) return E_FAIL;

			iptr<IKVirtalFile> tmp( PROGID_VirtualFile );
			if( !tmp) return E_FAIL;

			_mapping = new FileMapping;
			if( !_mapping ) return E_OUTOFMEMORY;

			RFAILED( nativ->Open( aFileName, OFRM ) );
			RFAILED( tmp->Open( IKVirtalFile::MODE_CACHEOFBDO, OFR, nativ ) );

			RFAILED( _reader.open( tmp ) );

			HANDLE hFileHandle = 0;
			RFAILED( nativ->GetHandle( &hFileHandle ) );

			HANDLE hMap = ::CreateFileMapping( hFileHandle, NULL, PAGE_READONLY,0, 0, NULL );
			if( !hMap ) return E_FAIL;

			_mapping->_NativFile = nativ;
			_mapping->_MapHandle = hMap;

			return S_OK;
		}
		
		STDMETHOD(LoadFromFile)( LPCWSTR wFileName, UINT32 opt )
		{
			iptr<IKNativeFile> nativ( PROGID_WinNativeFile );
			if( !nativ ) return E_FAIL;

			iptr<IKVirtalFile> tmp( PROGID_VirtualFile );
			if( !tmp) return E_FAIL;

			_mapping = new FileMapping;
			if( !_mapping ) return E_OUTOFMEMORY;

			RFAILED( nativ->Open( wFileName, OFRM ) );
			RFAILED( tmp->Open( IKVirtalFile::MODE_CACHEOFBDO, OFR, nativ ) );

			RFAILED( _reader.open( tmp ) );

			HANDLE hFileHandle = 0;
			RFAILED( nativ->GetHandle( &hFileHandle ) );

			HANDLE hMap = ::CreateFileMapping( hFileHandle, NULL, PAGE_READONLY, 0, 0, NULL );
			if( !hMap ) return E_FAIL;

			_mapping->_NativFile = nativ;
			_mapping->_MapHandle = hMap;

			return S_OK;
		}
		STDMETHOD(FindTable)( LPCSTR aTabName, IKMDRTable** ppTable )
		{
			refp<TabReader::sect_t> sectp = _reader.find_section( aTabName );
			if( !sectp ) return E_FAIL;

			iptr<IKMDRTable> otab( sectp->ud.ip<IKMDRTable>() );
			if( !otab )
			{
				optr<MdTable> otab2 = new MdTable;
				if( !otab2 ) return E_OUTOFMEMORY;

				RFAILED( otab2->init( _mapping, sectp ) );

				sectp->ud = otab2.p();
				otab = sectp->ud.ip<IKMDRTable>();
			}
			*ppTable = otab.detach();
			return S_OK;
		}
	};



};


#endif